package cn.stylefeng.guns.cloud.workflow.modular.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.guns.cloud.libs.mp.page.PageFactory;
import cn.stylefeng.guns.cloud.model.exp.RequestEmptyException;
import cn.stylefeng.guns.cloud.model.exp.ServiceException;
import cn.stylefeng.guns.cloud.model.page.PageResult;
import cn.stylefeng.guns.cloud.workflow.modular.model.params.ModelParam;
import cn.stylefeng.guns.cloud.workflow.modular.model.result.ModelResult;
import cn.stylefeng.guns.cloud.workflow.modular.service.ModelService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 模型服务 实现类
 *
 * @Author xuyuxiang
 * @Date 2019/11/14 14:48
 **/
@Service
@Slf4j
public class ModelServiceImpl implements ModelService {

    private final String EDITOR_NODE_ID = "canvas";
    private final String EDITOR_RESOURCE_ID = "canvas";
    private final String STENCIL_SET_NODE_NAMESPACE = "http://b3mn.org/stencilset/bpmn2.0#";
    private final String MODEL_EDIT_PAGE_PREFIX = "/activiti-editor/modeler.html?modelId=";

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 添加模型
     *
     * @Author xuyuxiang
     * @Date 2019/10/25 15:33
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String add(ModelParam modelParam) {

        String name = modelParam.getName();

        String key = modelParam.getKey();

        String description = modelParam.getDescription();

        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode editorNode = objectMapper.createObjectNode();

        editorNode.put("id", EDITOR_NODE_ID);

        editorNode.put("resourceId", EDITOR_RESOURCE_ID);

        ObjectNode stencilSetNode = objectMapper.createObjectNode();

        stencilSetNode.put("namespace", STENCIL_SET_NODE_NAMESPACE);

        editorNode.set("stencilset", stencilSetNode);

        Model modelData = repositoryService.newModel();

        ObjectNode modelObjectNode = objectMapper.createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, name);
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        description = StringUtils.defaultString(description);
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
        modelData.setMetaInfo(modelObjectNode.toString());
        modelData.setName(name);
        modelData.setKey(StringUtils.defaultString(key));
        repositoryService.saveModel(modelData);

        String modelId = modelData.getId();
        try {
            repositoryService.addModelEditorSource(modelId, editorNode.toString().getBytes("utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return MODEL_EDIT_PAGE_PREFIX + modelId;
    }

    /**
     * 删除模型
     *
     * @Author xuyuxiang
     * @Date 2019/10/25 15:43
     **/
    @Override
    public void delete(String modelId) {
        repositoryService.deleteModel(modelId);
    }

    /**
     * 查询模型列表
     *
     * @return
     * @Author xuyuxiang
     * @Date 2019/10/25 16:03
     */
    @Override
    public PageResult list(ModelParam modelParam) {
        Page pageContext = getPageContext();

        ModelQuery modelQuery = repositoryService.createModelQuery();

        if (ObjectUtil.isNotEmpty(modelParam.getName())) {
            modelQuery.modelNameLike("%" + modelParam.getName() + "%");
        }
        if (ObjectUtil.isNotEmpty(modelParam.getKey())) {
            modelQuery.modelKey(modelParam.getKey());
        }
        List<Model> models = modelQuery
                .listPage((int) pageContext.getSize() * ((int) pageContext.getCurrent() - 1),
                        (int) pageContext.getSize());
        long count = modelQuery.count();
        List<ModelResult> list = new ArrayList<>();
        for (Model model : models) {
            ModelResult modelResult = new ModelResult();
            BeanUtil.copyProperties(model, modelResult);
            list.add(modelResult);
        }
        pageContext.setRecords(list);

        pageContext.setTotal(count);
        return new PageResult(pageContext);
    }

    /**
     * 部署流程
     *
     * @Author xuyuxiang
     * @Date 2019/10/28 15:18
     **/
    @Override
    public void deploy(String modelId) {
        try {
            Model modelData = repositoryService.getModel(modelId);
            byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());

            if (bytes == null) {
                throw new RequestEmptyException();
            }
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            if (model.getProcesses().size() == 0) {
                throw new RequestEmptyException();
            }
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
            //发布流程
            String processName = modelData.getName() + ".bpmn20.xml";
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addString(processName, new String(bpmnBytes, "UTF-8"))
                    .deploy();
            modelData.setDeploymentId(deployment.getId());
            repositoryService.saveModel(modelData);
        } catch (Exception e) {
            log.error("部署流程失败！", e);
            throw new ServiceException(500, "部署流程失败！");
        }
    }

    /**
     * 预览xml
     *
     * @Author xuyuxiang
     * @Date 2019/11/19 11:56
     **/
    @Override
    public String preView(String modelId) throws IOException {

        if (this.isCanExportXml(modelId)) {
            Model modelData = repositoryService.getModel(modelId);

            boolean canPreview = modelData.hasEditorSourceExtra();
            if (!canPreview) {
                throw new ServiceException(1001, "无流程文件资源");
            }

            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();

            ObjectNode modelNode = (ObjectNode) new ObjectMapper()
                    .readTree(repositoryService.getModelEditorSource(modelData.getId()));

            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(modelNode);

            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();

            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);

            InputStreamReader isr = null;
            try {
                isr = new InputStreamReader(in, "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            BufferedReader bufferedReader = new BufferedReader(isr);

            StringBuffer xmlContent = new StringBuffer();

            String lineTxt = null;

            while (true) {
                try {
                    if (!((lineTxt = bufferedReader.readLine()) != null)) break;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                xmlContent.append(lineTxt);
                xmlContent.append("\n");
            }
            isr.close();

            return xmlContent.toString();
        }
        return null;
    }

    /**
     * 是否能导出
     *
     * @Author xuyuxiang
     * @Date 2019/11/19 11:57
     **/
    public boolean isCanExportXml(String modelId) {
        try {
            Model modelData = repositoryService.getModel(modelId);

            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();

            ObjectNode modelNode = (ObjectNode) new ObjectMapper()
                    .readTree(repositoryService.getModelEditorSource(modelData.getId()));

            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(modelNode);

            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();

            xmlConverter.convertToXML(bpmnModel);

            return true;
        } catch (Exception e) {
            log.info("导出xml为空！不能导出！");
            return false;
        }
    }

    /**
     * 获取pageContext
     *
     * @Author xuyuxiang
     * @Date 2019/10/25 16:04
     **/
    private Page getPageContext() {
        return PageFactory.defaultPage();
    }
}
